﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LevelEditor.events;
using LevelEditor.model.elements;
using System.Drawing;

namespace LevelEditor.model.core
{
    class EditorModel
    {
        #region Variables
        private List<DoublePage> m_doublePagesList;
        private int m_doublePagesNb;
        private int m_currentDoublePageIndex;
       
        #endregion

        #region Constructeurs
        public EditorModel()
        {
            m_doublePagesNb = 0;
            m_currentDoublePageIndex = 0;
            m_doublePagesList = new List<DoublePage>();
        }

        #endregion

        #region Methodes appelees par le controleur - IO
        public void loadLibrary()
        {
            Library.load();
            Library.init();
            onLibraryChanged(this, new LibraryChangedEvent("Librairie chargee!", Library.libNamesList, Library.getBmp(0)));
        }

        public void loadLevel(string levelFilePath)
        {
            Library.clearUsedAssetsList();
            m_doublePagesList = ParseXML.readXML(levelFilePath);

            if (m_doublePagesList == null || m_doublePagesList.Count == 0)
            {
                Console.WriteLine("Impossible de charger le niveau " + levelFilePath);
                return;
            }

            m_doublePagesNb = m_doublePagesList.Count;
            m_currentDoublePageIndex = 0;

            string msg = "Level de " + m_doublePagesNb + " pages charges !";
            onLevelChanged(this, new LevelChangedEvent(msg, m_doublePagesList, m_doublePagesNb, 1));
        }

        public void save(String url)
        {
            ParseXML.writeXML(url, m_doublePagesList);
        }

        #endregion

        #region Modifications sur les pages
        public void addNewDoublePage()
        {
            DoublePage doublePage = new DoublePage();
            m_doublePagesList.Add(doublePage);
            m_currentDoublePageIndex = m_doublePagesNb;
            m_doublePagesNb++;

            string msg = "Nouvelle DoublePage creee => " + (m_currentDoublePageIndex + 1) + " / " + m_doublePagesNb;
            onDoublePageChanged(this, new DoublePageChangedEvent(msg, doublePage, m_currentDoublePageIndex, 0, m_doublePagesNb));
        }

        public void switchToNextDoublePage()
        {
            m_currentDoublePageIndex = (m_currentDoublePageIndex + 1) % m_doublePagesNb;
            string msg = "Switch to next double page " + (m_currentDoublePageIndex + 1) + " / " + m_doublePagesNb;
            onDoublePageChanged(this, new DoublePageChangedEvent(msg, m_doublePagesList[m_currentDoublePageIndex], m_currentDoublePageIndex, 1, m_doublePagesNb));
        }

        public void switchToPreviousDoublePage()
        {
            m_currentDoublePageIndex = (m_currentDoublePageIndex - 1 + m_doublePagesNb) % m_doublePagesNb;
            string msg = "Switch to previous double page " + (m_currentDoublePageIndex + 1) + " / " + m_doublePagesNb;
            onDoublePageChanged(this, new DoublePageChangedEvent(msg, m_doublePagesList[m_currentDoublePageIndex], m_currentDoublePageIndex, 1, m_doublePagesNb));
        }

        public void removeCurrentDoublePage()
        {
            m_doublePagesList.RemoveAt(m_currentDoublePageIndex);
            
            if( m_currentDoublePageIndex == ( m_doublePagesNb - 1 ) )
            {
                m_currentDoublePageIndex--;
            }

            m_doublePagesNb--;

            string msg = "DoublePage courante supprimee => " + (m_currentDoublePageIndex + 1) + " / " + m_doublePagesNb;
            onDoublePageChanged(this, new DoublePageChangedEvent(msg, m_doublePagesList[m_currentDoublePageIndex], m_currentDoublePageIndex, 2, m_doublePagesNb));
        }

        public void resetLevel()
        {
            m_doublePagesList.Clear();
            m_doublePagesNb = 0;

            Library.usedAssetsList.Clear();
            PGameObjectsFactory.resetPGameObjectsCreatedNb();

            onLevelChanged(this, new LevelChangedEvent("New Level", m_doublePagesList, m_doublePagesNb, 0));
            addNewDoublePage();
        }

        #endregion

        #region Modifications sur les pGameObjects
        public void addNewPGameObject(int key, int index)
        {
            AbstractPGameObject pGameObject = PGameObjectsFactory.createPGameObject(key);
            
            if (pGameObject != null)
            {
                String usedAssetXmlFileName = Library.getXmlLinkedToUrl(pGameObject.picture.url);
                Library.addUsedAsset(usedAssetXmlFileName);

                pGameObject.resId = Library.getResIdFromXmlFileName(usedAssetXmlFileName);

                if (pGameObject.resId == -1)
                {
                    onStatutChanged(this, new StatutChangedEvent("Impossible de creer un PGameObject car resId == -1 " + pGameObject.resId));
                }

                m_doublePagesList[m_currentDoublePageIndex].addPGameObject(pGameObject);
                
                string msg = "Nouveau PGameObject " + index + " " + pGameObject.name + " ajoute a la page courante " + m_currentDoublePageIndex;
                onPGameObjectSelected(this, new PGameObjectSelectedEvent(msg, pGameObject));
            }
            else
            {
                onStatutChanged(this, new StatutChangedEvent("Impossible de creer un PGameObject car type inconnu!"));
            }
        }

        public void removePGameObject(int index)
        {
            m_doublePagesList[m_currentDoublePageIndex].removePGameObject(index);
            Console.WriteLine("Suppression du PGameObject " + index + " de la page courante " + m_currentDoublePageIndex);
        }

        public void selectPGameObject(int index)
        {
            AbstractPGameObject pGameObject = m_doublePagesList[m_currentDoublePageIndex].getPGameObject(index);

            // TODO: Supprimer si c'est good
            if (pGameObject == null)
            {
                Console.WriteLine("Impossible de recuperer l'object " + index + " de la page courante " + m_currentDoublePageIndex);
                return;
            }

            string msg = "Selection du PGameObject " + index + " de la page courante " + m_currentDoublePageIndex;
            onPGameObjectSelected(this, new PGameObjectSelectedEvent(msg, pGameObject));
        }

        // TODO: Factoriser les deux methodes de changement de parametres avec type generique ?
        public void changePGameObjectParams(int index, string boxName , float value)
        {
            AbstractPGameObject pGameObject = m_doublePagesList[m_currentDoublePageIndex].getPGameObject(index);

            // TODO: Supprimer si c'est good
            if (pGameObject == null)
            {
                Console.WriteLine("Impossible de recuperer l'object " + index + " de la page courante " + m_currentDoublePageIndex);
                return;
            }

            // Code qui change les proprietes du pGameOBject concerne
            if (boxName.Equals("textBoxX"))
            {
                pGameObject.x = value;
            }
            else if(boxName.Equals("textBoxY"))
            {
                pGameObject.y = value;
            }
            else if (boxName.Equals("textBoxScaleX"))

            {
                pGameObject.scaleX = value;
            }
            else if(boxName.Equals("textBoxScaleY"))
            {
                pGameObject.scaleY = value;
            }
            else if (boxName.Equals("textBoxAngle"))
            {
                pGameObject.orientationAngle = value;
            }

            string msg = "Modification du PGameObject " + index + " de la page courante " + m_currentDoublePageIndex;
            onPGameObjectChanged(this, new PGameObjectChangedEvent(msg, pGameObject, 0));
        }

        public void changePGameObjectParams(int index, string boxName, string value)
        {
            AbstractPGameObject pGameObject = m_doublePagesList[m_currentDoublePageIndex].getPGameObject(index);

            // TODO: Supprimer si c'est good
            if (pGameObject == null)
            {
                Console.WriteLine("Impossible de recuperer l'object " + index + " de la page courante " + m_currentDoublePageIndex);
                return;
            }

            // Code qui change les proprietes du pGameOBject concerne
            if (boxName.Equals("textBoxName"))
            {
                pGameObject.name = value;
            }

            string msg = "Modification du PGameObject " + index + " de la page courante " + m_currentDoublePageIndex;
            onPGameObjectChanged(this, new PGameObjectChangedEvent(msg, pGameObject, 0));
        }

        public void addLinkedPGO(int focusPGOindex, int focusPGOpageIndex, int linkedPGOindex, int linkedPGOPageIndex)
        {
            AbstractPGameObject linkedPGO = m_doublePagesList[ linkedPGOPageIndex ].getPGameObject( linkedPGOindex );
            AbstractPGameObject pGameObject = m_doublePagesList[focusPGOpageIndex].getPGameObject(focusPGOindex);

            // TODO: Supprimer si c'est good
            if (pGameObject == null)
            {
                Console.WriteLine("Impossible de recuperer l'object " + focusPGOindex + " de la page " + m_currentDoublePageIndex);
                return;
            }

            pGameObject.addLinkedPGO(linkedPGO);

            string msg = "Ajout d'un linkedPGO au PGameObject " + focusPGOindex + " de la page " + focusPGOpageIndex;
            onPGameObjectChanged(this, new PGameObjectChangedEvent(msg, pGameObject, 0));
        }
        #endregion

        #region Selection dans la lib
        public void selectLibraryObject(int index)
        {
            string msg = "Object " + index + " de la library selectionne";
            onLibraryChanged(this, new LibraryChangedEvent(msg, Library.libNamesList, Library.getBmp(index)));
        }

        #endregion

        #region Methodes pour gerer les evenements
        public delegate void StatutEventHandler(object sender, StatutChangedEvent e);
        public delegate void LibraryEventHandler(object sender, LibraryChangedEvent e);
        public delegate void PGameObjectEventHandler(object sender, PGameObjectChangedEvent e);
        public delegate void PGameObjectSelectedEventHandler(object sender, PGameObjectSelectedEvent e);
        public delegate void DoublePageEventHandler(object sender, DoublePageChangedEvent e);
        public delegate void LevelEventHandler(object sender, LevelChangedEvent e);

        public event StatutEventHandler onStatutChanged;
        public event LibraryEventHandler onLibraryChanged;
        public event PGameObjectEventHandler onPGameObjectChanged;
        public event PGameObjectSelectedEventHandler onPGameObjectSelected;
        public event DoublePageEventHandler onDoublePageChanged;
        public event LevelEventHandler onLevelChanged;

        #endregion

        #region Getters && Setters
        public int doublePageNb
        {
            get { return m_doublePagesNb; }
        }

        #endregion
    }
}
